home *** CD-ROM | disk | FTP | other *** search
- /**
- ** EvalText.m
- **
- ** A text subclass which provides emacs key bindings
- ** and "colored" program text.
- **/
-
- #import "MiscEmacsText.h"
-
- #import <stdlib.h>
- #import <strings.h>
- #import <appkit/appkit.h>
- #import <defaults/defaults.h>
- #import <libc.h>
- #import <ctype.h>
-
-
- #define ESC (27)
- #define CTRL_SPACE (0)
- #define CTRL_A (1)
- #define CTRL_B (2)
- #define CTRL_C (3)
- #define CTRL_D (4)
- #define CTRL_E (5)
- #define CTRL_F (6)
- #define CTRL_K (11)
- #define CTRL_L (12)
- #define CTRL_N (14)
- #define CTRL_O (15)
- #define CTRL_P (16)
- #define CTRL_S (19)
- #define CTRL_V (22)
- #define CTRL_W (23)
- #define CTRL_X (24)
- #define CTRL_Y (25)
-
- /**
- ** keyWords[] is a structure used by format: sender to
- ** identify keyWords in transcript text
- **/
- static struct
- { char * word ;
- int len ;
- } keyWords[] =
- { {"int",3},
- {"char",4},
- {"float",5},
- {"double",6},
- {"struct",6},
- {"union",5},
- {"long",4},
- {"short",5},
- {"unsigned",8},
- {"auto",4},
- {"extern",6},
- {"register",8},
- {"typedef",7},
- {"static",6},
- {"goto",4},
- {"return",6},
- {"sizeof",6},
- {"break",5},
- {"continue",8},
- {"if",2},
- {"else",4},
- {"for",3},
- {"do",2},
- {"while",5},
- {"switch",6},
- {"case",4},
- {"default",7},
- {"entry",5}, /* is this a keyword in ansi C ? */
- {"const",5},
- {"id",2},
- {"void",4},
- {NULL,0}
- } ;
-
-
- /**
- ** Fragments of the following procedures were borrowed from
- ** Lee boynton's LispExample program, then hacked to bits.
- **/
-
- static char killedText[10000] ; // buffer for "killed" text...
- // since it is statically allocated, this thing could blow up
- // if > 10000 chars are written to the kill buffer. If I had
- // I'd make this grow as needed....
-
- static BOOL useEmacsBindings = 1 ;
-
- typedef struct {
- id text;
- NXTextBlock *block;
- } textInfo;
-
- BOOL evalGets(NXStream *aStream, char * aBuf)
- { // Procedure to get the next line (or remainder of
- // stream) from a stream. The newline is discarded.
- // If newline is present, returns YES, else if instead
- // the end of stream is found, returns NO (but aBuf may
- // still contain data).
- int c,i = 0;
- c = NXGetc(aStream) ;
- do
- { if(c == -1) // end of stream
- { aBuf[i] = '\0' ;
- return NO ;
- }
- else if(c == '\n')
- { aBuf[i] = '\0' ;
- return YES ;
- }
- else
- { aBuf[i++] = (char) c ;
- c = NXGetc(aStream) ;
- }
- } while(1) ;
- }
-
- static char getPrevious(NXStream *s)
- { // NXBGetc - a text stream macro to get the previous character.
- textInfo *info = (textInfo *) s->info;
- NXTextBlock *block = info->block->prior;
- if (!block)
- { return EOF;
- }
- s->buf_base = block->text;
- s->buf_ptr = s->buf_base + block->chars;
- s->offset -= block->chars;
- info->block = block;
- return *(--s->buf_ptr);
- }
-
- #define NXBGetc(s) \
- (((s)->buf_base == (s)->buf_ptr) ? getPrevious(s) : \
- *(--((s)->buf_ptr)) )
-
-
- int findMatchingOpenParen(id text, char thisChar, char thatChar, int curPos)
- { int count = 1;
- char ch;
- NXStream *s = [text stream];
- NXSeek(s, curPos, NX_FROMSTART);
- while ((ch = NXBGetc(s)) != EOF)
- { if(ch == thatChar && !(--count))
- return NXTell(s);
- else if (ch == thisChar)
- count++;
- }
- return -1;
- }
-
-
- int
- findMatchingCloseParen(id text, char thisChar, char thatChar, int curPos)
- { int count = 1;
- char ch;
- NXStream *s = [text stream];
- NXSeek(s, curPos, NX_FROMSTART);
- while ((ch = NXGetc(s)) != EOF)
- { if(ch == thatChar && !(--count))
- return NXTell(s);
- else if (ch == thisChar)
- count++;
- }
- return -1;
- }
-
- void highlightChar(id text, int charPosition)
- { NXSelPt selStart, selEnd;
- [text getSel:&selStart :&selEnd];
- [text setSel:charPosition :charPosition+1];
- [[text window] flushWindow];
- NXPing();
- [[text window] disableFlushWindow];
- [text setSel:selStart.cp :selEnd.cp];
- [[text window] reenableFlushWindow];
- }
-
-
- @implementation MiscEmacsText:Text
-
-
- // macros which write rtf to a stream called "cookedStream"
- // for creating fancy text for various code categories
- #define PREPROC NXPrintf(cookedStream,"\\f0%s\\cf0%s ",\
- fontStuff[0][0],fontStuff[0][2])
- #define DIRECTIVE NXPrintf(cookedStream,"\\f1%s\\cf1%s ",\
- fontStuff[1][0],fontStuff[1][2])
- #define METHODDEF NXPrintf(cookedStream,"\\f2%s\\cf2%s ",\
- fontStuff[2][0],fontStuff[2][2])
- #define COMMENT NXPrintf(cookedStream,"\\f3%s\\cf3%s ",\
- fontStuff[3][0],fontStuff[3][2])
- #define STRING NXPrintf(cookedStream,"\\f4%s\\cf4%s ",\
- fontStuff[4][0],fontStuff[4][2])
- #define KEYWORD NXPrintf(cookedStream,"\\f5%s\\cf5%s ",\
- fontStuff[5][0],fontStuff[5][2])
- #define NORMAL NXPrintf(cookedStream,"\\f6%s\\cf6%s ",\
- fontStuff[6][0],fontStuff[6][2])
-
- #define TEXTDEFAULTKNT 7 /* number of text categories */
-
- - awakeFromNib ;
- { // Do some setup work...I am my own delegate
- // [self setDelegate: self] ;
-
- NXAtom textTypes[5];
-
- textTypes[0] = NXRTFPboardType;
- textTypes[1] = NXAsciiPboardType;
- textTypes[2] = NULL;
-
- [self registerForDraggedTypes:textTypes count:2];
- return self;
- }
-
- - clearMessage: sender ;
- { // Clear the message in the transcript. Called by keydown
- // via a delayed message send.
- //
- // [window message: ""] ;
- return self ;
- }
-
- - format: sender ;
- { // Color and set font attributes for text in the
- // transcript. If sender responds to
- // to the methodList: method, (which must should return a List),
- // then allocates and adds an EvalString with the first line
- // of each method name to this methodlist.
- char textBuf[1024] ;
- char fontStuff[TEXTDEFAULTKNT][3][40], defName[12] ;
- const char *defaults ;
- int i, j, keyWordLen, charNum, braceLevel = 0 ;
- BOOL inString = NO, inComment = NO, inSlashSlashComment = NO,
- inMethodDef = NO, inPreProc = NO, newline;
- NXStream *rawStream, *cookedStream ;
- id methodList = nil ;
-
- /*
- if([sender respondsTo: @selector(methodList)])
- { methodList = [sender methodList] ;
- }
- */
-
- rawStream = [self stream] ;
- cookedStream = NXOpenMemory(NULL,0,NX_READWRITE) ;
- NXPrintf(cookedStream,"{\\rtf0\\ansi{\\fonttbl") ;
- // get the font information from defaults database
- for(i = 0 ; i < TEXTDEFAULTKNT ; i++)
- { // Manufacture default name
- sprintf(defName,"text%1d",i) ;
- defaults = NXGetDefaultValue([NXApp appName],defName) ;
- // parse the default value
- sscanf(defaults,"%s %s %s %s",
- textBuf, fontStuff[i][0], fontStuff[i][1],fontStuff[i][2]) ;
- NXPrintf(cookedStream,"\\f%1d\\fnil %s;", i,textBuf) ;
- }
- NXPrintf(cookedStream,"}\n") ;
- NXPrintf(cookedStream,"{\\colortbl") ;
- for(i = 0 ; i < TEXTDEFAULTKNT ; i++)
- { NXPrintf(cookedStream,fontStuff[i][1]) ;
- NXPrintf(cookedStream,";") ;
- }
- NXPrintf(cookedStream,"}\n") ;
- charNum = 0 ;
- NORMAL ;
- do
- { NXSeek(rawStream, charNum, NX_FROMSTART) ;
- newline = evalGets(rawStream,textBuf) ;
- inString = NO ; // ANSI C forbids newline in string
- // now examine the string, adding rtf directives as required
- for(i = 0 ; textBuf[i] ;)
- { switch(textBuf[i])
- { case '/':
- if(inString)
- NXPutc(cookedStream,textBuf[i++]) ;
- else if(textBuf[i + 1] == '*' || textBuf[i+1] == '/')
- { if(!inComment)
- { COMMENT ;
- inComment = YES ;
- if(textBuf[i+1] == '/') // behaves specially!
- inSlashSlashComment = YES ;
- }
- NXPutc(cookedStream,textBuf[i++]) ;
- }
- else
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- case '*':
- if(inComment && textBuf[i+1] == '/' && !inSlashSlashComment)
- { NXPutc(cookedStream,textBuf[i++]) ;
- NXPutc(cookedStream,textBuf[i++]) ;
- if(inMethodDef)
- METHODDEF ;
- else
- NORMAL ;
- inComment = NO ;
- }
- else
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- case '"':
- if(inComment || inPreProc)
- NXPutc(cookedStream,textBuf[i++]) ;
- else if(inString)
- { NXPutc(cookedStream,textBuf[i++]) ;
- if(textBuf[i - 2] != '\\')
- { inString = NO ;
- NORMAL ;
- }
- }
- else
- { if(i == 0 || textBuf[i - 1] != '\\')
- { inString = YES ;
- STRING ;
- }
- NXPutc(cookedStream,textBuf[i++]) ;
- }
- break ;
- case '}' :
- if(!inComment && !inString && !inPreProc)
- { if(braceLevel > 0) // don't allow this to go negative
- braceLevel-- ;
- }
- // must be escaped for rtf
- NXPutc(cookedStream,'\\') ;
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- case '\\':
- // must be escaped for rtf
- NXPutc(cookedStream,'\\') ;
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- case '{' :
- if(!inComment && !inString && !inPreProc)
- braceLevel++ ;
- if(braceLevel == 1 && inMethodDef)
- { inMethodDef = NO ;
- NORMAL ;
- }
- NXPutc(cookedStream,'\\') ;
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- case '#':
- if(!inComment && i == 0)
- { // a hash in column 0 means a preprocessor line
- PREPROC ;
- inPreProc = YES ;
- }
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- case '@':
- if(!inString && !inComment && !inPreProc)
- { // then this is a compiler directive
- DIRECTIVE ;
- // if((i == 0) && methodList) // if a compiler directive is in
- // column 0, put it into the methodList
-
- //
- //
- // [methodList addObject: [[EvalString alloc] init: textBuf]] ;
- //
- //
-
- NXPutc(cookedStream,textBuf[i++]) ;
- while(isalnum(textBuf[i]))
- NXPutc(cookedStream,textBuf[i++]) ;
- NORMAL ;
- }
- else
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- case '-':
- case '+':
- if(!inComment && !inString && !inPreProc && braceLevel == 0)
- { for(j = 0 ; j < i ; j++)
- if(!isspace(textBuf[j]) && textBuf[j] != '\\') break ;
- if(j == i)
- { // this is not a truly accurate way of finding the beginning of a
- // method def, but it will work "most" of the time...
- inMethodDef = YES ;
- METHODDEF ;
- //
- //
- // if(methodList)
- // [methodList addObject: [[EvalString alloc] init: textBuf]] ;
- //
- //
- //
-
- }
- }
- if(textBuf[i] == '-') // must be escaped
- NXPutc(cookedStream,'\\') ;
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- case ' ':
- case '\t':
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- default:
- if(!inComment && !inString && !inMethodDef && !inPreProc &&
- (i == 0 || !(isalnum(textBuf[i-1]) || textBuf[i-1] == '_')))
- { // check for a keyword
- for(j = 0 ; keyWordLen = keyWords[j].len ; j++)
- { if(!strncmp(&textBuf[i],keyWords[j].word,keyWordLen))
- { int followChar = i + keyWordLen ;
- int isKeyWord = !(isalnum(textBuf[followChar])
- || textBuf[followChar]=='_') ;
- if(isKeyWord)
- KEYWORD ;
- NXPrintf(cookedStream,"%s\n",keyWords[j].word) ;
- if(isKeyWord)
- NORMAL ;
- i += keyWordLen ;
- break ;
- }
- }
- if(!keyWordLen) // no keyWord match found
- NXPutc(cookedStream,textBuf[i++]) ;
- }
- else
- NXPutc(cookedStream,textBuf[i++]) ;
- break ;
- }
- }
- // check for a preprocessor continuation line
- if(inPreProc && textBuf[i - 1] != '\\')
- { inPreProc = NO ;
- NORMAL ;
- }
- // terminate a slash-slash style comment
- if(inSlashSlashComment)
- { inSlashSlashComment = inComment = NO ;
- if(inMethodDef)
- METHODDEF ;
- else
- NORMAL ;
- }
- if(newline) // output a quoted newline
- { NXPutc(cookedStream,'\\') ;
- NXPutc(cookedStream,'\n') ;
- }
- charNum += strlen(textBuf) + 1;
- } while(!NXAtEOS(rawStream)) ;
-
- NXPutc(cookedStream,'}') ;
- NXSeek(cookedStream, 0, NX_FROMSTART) ;
- [self readRichText: cookedStream] ;
- NXCloseMemory(cookedStream,NX_FREEBUFFER) ;
- return self ;
- }
-
-
- #define MARK_UNSET -1
-
- - keyDown:(NXEvent *)theEvent
- { // provide some emacs-style key bindings. We make
- // the assumption that we are subview of a scrollview.
- int i ;
- static unsigned short val = '\0', lastChar = '\0', nextChar ;
- static BOOL firstKey = TRUE, definingMacro = NO,
- insertChar, replayingMacro = NO ;
- static long mark = MARK_UNSET ;
- NXSelPt selStart, selEnd;
- static NXStream * macroStream ;
- if(!useEmacsBindings)
- return [super keyDown: theEvent] ;
-
- if(firstKey) // make sure killedText is empty
- { killedText[0] = '\0' ;
- firstKey = FALSE ;
- macroStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
- }
- [self getSel:&selStart :&selEnd];
- do
- { lastChar = val ; // val was set in "previous" call of keyDown..
- val = theEvent->data.key.charCode + (theEvent->data.key.charSet << 8);
- if(definingMacro)
- NXPutc(macroStream,theEvent->data.key.charCode) ;
- insertChar = YES ;
- switch ((unsigned char) val)
- { case ESC: // don't want ESC to cause a beep
- insertChar = NO ;
- break ;
- case '<':
- if(lastChar != ESC)
- break ;
- case 0xa3: // ALT-< or ESC <
- { [self setSel: 0 :0] ;
- [self scrollSelToVisible] ;
- insertChar = NO ;
- }
- break ;
- case '>':
- if(lastChar != ESC)
- break ;
- case 0xb3: // ALT-> or ESC >
- { i = [self textLength] ; --i ;
- [self setSel: i :i] ;
- [self scrollSelToVisible] ;
- insertChar = NO ;
- }
- break ;
- case '(':
- if((lastChar == CTRL_X) && !definingMacro)
- { // start defining macro
- // [window message: "Defining kbd macro..."] ;
- definingMacro = YES ;
- NXSeek(macroStream, 0, NX_FROMSTART) ;
- insertChar = NO ;
- }
- break ;
- case ')':
- if((lastChar == CTRL_X) && definingMacro)
- { // [window message: "Keyboard macro defined."] ;
- // [self perform: @selector(clearMessage:) with: self
- // afterDelay: 1500 cancelPrevious: YES] ;
- definingMacro = NO ;
- NXSeek(macroStream, -2, NX_FROMCURRENT) ; // seek back 2 to remove ctrl_X )
- NXPutc(macroStream,'\0') ; // null-terminate the stream
- insertChar = NO ;
- }
- else
- { i = findMatchingOpenParen(self,')','(',selEnd.cp);
- [super keyDown:theEvent];
- if (i >= 0)
- highlightChar(self,i);
- insertChar = NO ;
- }
- break ;
- case '}':
- i = findMatchingOpenParen(self,'}','{',selEnd.cp);
- [super keyDown:theEvent];
- if(i >= 0)
- highlightChar(self,i);
- insertChar = NO ;
- break ;
- case ']':
- i = findMatchingOpenParen(self,']','[',selEnd.cp);
- [super keyDown:theEvent];
- if (i >= 0)
- highlightChar(self,i);
- insertChar = NO ;
- break ;
- case 'e':
- if(lastChar == CTRL_X)
- { if(!definingMacro)
- { // replay macro
- replayingMacro = YES ;
- NXSeek(macroStream,0L,NX_FROMSTART) ;
- insertChar = NO ;
- }
- else
- NXSeek(macroStream, -1, NX_FROMCURRENT) ; // seek back 1 to remove ctrl_X
- }
- break ;
- case 'i':
- if(lastChar == CTRL_X)
- { if(!definingMacro)
- { // insert a file into the text
- int rval ;
- id openPanel = [OpenPanel new] ;
- [openPanel allowMultipleFiles: NO] ;
- rval = [openPanel runModalForTypes: NULL] ;
- if(rval == NX_OKTAG)
- { NXStream *fileStream ;
- char fName[2048] ;
- sprintf(fName,"%s/%s",[openPanel directory],*[openPanel filenames]) ;
- if((fileStream = NXMapFile(fName,NX_READONLY)) != NULL)
- { int len,unused ;
- char *buf ;
- // [window message: "Inserting file..."] ;
- NXGetMemoryBuffer(fileStream,&buf,&len,&unused) ;
- [self replaceSel: buf length: len] ;
- // [window message: ""] ;
- }
- else
- NXRunAlertPanel("Eval","Couldn't insert file: %s",NULL,NULL,NULL,fName) ;
- }
- }
- insertChar = NO ;
- }
- break ;
-
- case 'k': // CTRL_X k = close window
- if(lastChar == CTRL_X)
- { [window performClose: self] ;
- insertChar = NO ;
- }
- break ;
- case 's':
- if(lastChar == CTRL_X)
- { [window save: self] ;
- insertChar = NO ;
- }
- break ;
- case 'v':
- if(lastChar != ESC)
- break ;
- case 0xd6: // ALT-v or ESC v
- { // move up one screenful
- NXPoint scrollPoint = {0.0,0.0} ;
- NXRect clipRect ;
- [self convertPoint: &scrollPoint fromView: [superview superview]] ;
- // get the clipview's bounds
- [superview getBounds: &clipRect] ;
- scrollPoint.y -= clipRect.size.height ; // scroll up
- [self scrollPoint: &scrollPoint] ;
- insertChar = NO ;
- }
- break ;
- case 'w':
- if(lastChar != ESC)
- break ;
- case 0xc8: // ALT-w or ESC w
- { int subStringLength ;
- if(mark == MARK_UNSET)
- break ;
- [self getSel:&selStart :&selEnd];
- if(selStart.cp < mark)
- [self getSubstring: killedText
- start: selStart.cp length: subStringLength = mark - selStart.cp] ;
- else
- [self getSubstring: killedText
- start: mark length: subStringLength = selStart.cp - mark] ;
- killedText[subStringLength] = '\0' ;
- insertChar = NO ;
- }
- break ;
- case CTRL_SPACE: // set mark
- [self getSel:&selStart :&selEnd];
- mark = selStart.cp ;
- insertChar = NO ;
- break ;
- case CTRL_A: // beginning of line
- [self getSel:&selStart :&selEnd];
- i = [self lineFromPosition:selStart.cp] ;
- i = [self positionFromLine: i] ;
- [self setSel:i :i];
- insertChar = NO ;
- break ;
- case CTRL_C:
- if(lastChar == CTRL_X)
- { [NXApp terminate: self] ;
- insertChar = NO ;
- }
- break ;
- case CTRL_E: // end of line
- [self getSel:&selStart :&selEnd];
- i = [self lineFromPosition: selStart.cp] ;
- i = [self positionFromLine: i + 1] ; // beginning of next line
- if(i == -1) // then there is no next line...
- i = [self textLength] + 1 ;
- [self setSel:i -1 :i - 1];
- insertChar = NO ;
- break ;
- case CTRL_B:// backwards one char
- [self getSel:&selStart :&selEnd];
- if(selStart.cp > 1)
- [self setSel:selStart.cp - 1 :selStart.cp -1];
- else
- NXBeep();
- insertChar = NO ;
- break ;
- case CTRL_F: // CTRL_X CTRL_F = open file, CTRL_F forwards one char
- if(lastChar == CTRL_X)
- { [NXApp open: self] ;
- }
- else
- { [self getSel:&selStart :&selEnd];
- if(selEnd.cp < [self textLength])
- [self setSel:selEnd.cp+1 :selEnd.cp+1];
- else
- NXBeep();
- }
- insertChar = NO ;
- break ;
- case CTRL_D: // delete char under cursor
- [self getSel:&selStart :&selEnd];
- [self setSel:selEnd.cp :selEnd.cp+1];
- [self replaceSel:""];
- [self setSel:selStart.cp :selStart.cp] ;
- insertChar = NO ;
- break ;
- case CTRL_K: // clear to end of line, place killed text in killedText
- // if line is empty, then delete the newline (and place at end of killedText)
- { int currentEnd = 0 ;
- [self getSel:&selStart :&selEnd];
- i = [self lineFromPosition: selStart.cp] ;
- i = [self positionFromLine: i + 1] ; // i is index of beginning of next line
- if(i == -1) // then there is no next line...
- { i = [self textLength] ;
- }
- else // there is another line
- { if(i - selStart.cp > 1) // if line is not empty,
- i-- ; // don't include the newline.
- }
- // successive CTRL_K's append to killedText ; any other char
- // interrupts the sequence
- if(lastChar == CTRL_K)
- currentEnd = strlen(killedText) ;
- [self getSubstring: &killedText[currentEnd]
- start:selStart.cp length:i- selStart.cp] ;
- killedText[i - selStart.cp + currentEnd] = '\0' ;
- [self setSel:selStart.cp :i];
- [self replaceSel:""];
- [self setSel:selStart.cp :selStart.cp] ;
- insertChar = NO ;
- break ;
- }
- case CTRL_L: // center the current selection
- { // get the selection's location in the scrollview's coordinates
- NXPoint loc, scrollPoint = {0.0,0.0} ;
- NXRect scrollRect ;
- float delta ;
- id sView = [[self superview] superview] ;
- [self getSel:&selStart :&selEnd];
- loc.x = selStart.x ;
- loc.y = selStart.y ;
- [self convertPoint: &loc toView: sView] ;
- // get the scrollview's bounds
- [sView getBounds: &scrollRect] ;
- // how far is loc.y from middle of scrollview?...reset delta to this value
- delta = scrollRect.size.height / 2.0 - loc.y ;
- [sView convertPoint: &scrollPoint toView: self] ;
- scrollPoint.y -= delta ;
- [self scrollPoint: &scrollPoint] ;
- insertChar = NO ;
- break ;
- }
- case CTRL_O: // "open" a line...i.e. add blank line before current
- [self getSel:&selStart :&selEnd];
- [self setSel: selStart.cp :selStart.cp] ;
- [self replaceSel: "\n"] ;
- [self setSel: selStart.cp :selStart.cp] ;
- insertChar = NO ;
- break ;
- case CTRL_N: // move directly up or down one line
- case CTRL_P: // move directly up one line...fake up arrow
- { NXEvent fakeEvent ;
- bcopy((char *) theEvent,(char *) &fakeEvent,sizeof(NXEvent)) ;
- fakeEvent.data.key.charCode = val == CTRL_N ? 175 :173 ;
- fakeEvent.data.key.charSet = 1 ;
- fakeEvent.flags = NX_NUMERICPADMASK ;
- [super keyDown: &fakeEvent] ;
- insertChar = NO ;
- }
- break ;
- case CTRL_S: // ctrl x ctrl s = save
- if(lastChar == CTRL_X)
- { [window save: self] ;
- insertChar = NO ;
- }
- else
- {
- //
- //
- //
- // extern id finderObject ; // declared in Finder.m
- //
- // [finderObject doFind: self] ;
- //
- //
- }
- break ;
- case CTRL_V: // move down one screenful
- { NXPoint scrollPoint = {0.0,0.0} ;
- NXRect clipRect ;
-
- [self convertPoint: &scrollPoint fromView: [superview superview]] ;
- // get the clipview's bounds
- [superview getBounds: &clipRect] ;
- scrollPoint.y += clipRect.size.height ; // scroll down
- [self scrollPoint: &scrollPoint] ;
- insertChar = NO ;
- break ;
- }
- case CTRL_W: // delete text from point to mark ; add to killedText
- if(mark == MARK_UNSET)
- break ;
- [self getSel:&selStart :&selEnd];
- if(selStart.cp < mark)
- { [self getSubstring: killedText
- start: selStart.cp length:mark - selStart.cp] ;
- [self setSel: selStart.cp :mark] ;
- }
- else
- { [self getSubstring: killedText
- start: mark length: selStart.cp - mark] ;
- [self setSel: mark :selStart.cp] ;
- }
- [self replaceSel: ""] ;
- insertChar = NO ;
- break ;
- case CTRL_X: // don't want CTRL_X to cause a beep
- insertChar = NO ;
- break ;
- case CTRL_Y: // yank killedText
- [self getSel:&selStart :&selEnd];
- [self setSel:selStart.cp :selStart.cp] ;
- [self replaceSel: killedText] ;
- insertChar = NO ;
- break ;
- default:
- break;
- }
- if(insertChar)
- [super keyDown: theEvent] ;
- if(replayingMacro)
- { nextChar = NXGetc(macroStream) ;
- if(nextChar == '\0')
- { replayingMacro = NO ;
- NXPing() ;
- }
- else
- theEvent->data.key.charCode = nextChar ;
- }
- } while(replayingMacro) ;
- return self ;
- }
-
-
- - mouseDown: (NXEvent *) anEvent ;
- { // augment double-click behavior to
- // check for bracketed structures and quoted
- // items. augment command-click behaviour to
- // select between backquotes.
- NXSelPt start, end ;
- char openers[] = "{([<\"'/`" ;
- char closers[] = "})]>\"'/`" ;
- char c,d, *place ;
- int otherPos ;
- NXStream *aStream ;
- if(anEvent->data.mouse.click == 1)
- [super mouseDown: anEvent] ;
- else if(anEvent->data.mouse.click == 2)
- { aStream = [self stream] ;
- [self getSel: &start :&end] ;
- if(start.cp > 0)
- { NXSeek(aStream,start.cp - 1,NX_FROMSTART) ;
- c = NXGetc(aStream) ;
- if(place = index(closers,c)) // we have right side of a structure
- { d = openers[(int) place - (int) closers] ;// grab matching char
- if( (otherPos = findMatchingOpenParen(self, c, d, start.cp -1))
- != -1)
- { if(c == '`') // put selection "inside" ` ... ` pair
- [self setSel: otherPos + 1 :start.cp - 1] ;
- else
- [self setSel: otherPos :start.cp] ;
- return self ;
- }
- else
- [super mouseDown: anEvent] ;
- }
- else
- { NXSeek(aStream,start.cp,NX_FROMSTART) ;
- c = NXGetc(aStream) ;
- if(place = index(openers,c)) // we have left side of a structure
- { d = closers[(int) place - (int) openers] ;
- if( (otherPos = findMatchingCloseParen(self, c, d, start.cp +1))
- != -1)
- { if(c == '`') // put selection "inside" ` ... ` pair
- [self setSel: start.cp +1 :otherPos-1] ;
- else
- [self setSel: start.cp :otherPos] ;
- }
- }
- else
- [super mouseDown: anEvent] ;
- }
- }
- }
- else // deal with triple, quadruple, etc. clicks here...
- [super mouseDown: anEvent] ;
- if((anEvent->data.mouse.click == 1) &&
- (anEvent->flags & NX_COMMANDMASK))
- { // command click == search back for backquote (or beginning of text)
- // forward for backquote (or end ot text), then select.
- int from, to ;
- aStream = [self stream] ;
- [self getSel: &start :&end] ;
- if(start.cp > 0)
- { from = start.cp - 1 ;
- NXSeek(aStream,from,NX_FROMSTART) ;
- while(from >= 1 && ((c = NXBGetc(aStream)) != '`'))
- from-- ;
- to = start.cp ;
- NXSeek(aStream,to,NX_FROMSTART) ;
- while(!NXAtEOS(aStream) && ((c = NXGetc(aStream)) != '`'))
- to++ ;
- [self setSel: from :to] ;
- }
- }
- return self ;
- }
-
- /*
- - textDidGetKeys: sender isEmpty: (BOOL) flag
- { [window setDocEdited: YES] ;
- return self ;
- }
- */
-
- - useEmacsBindings: (BOOL) YESorNO ;
- { useEmacsBindings = YESorNO ;
- return self ;
- }
-
- @end
-
- @implementation MiscEmacsText(Dropping)
-
- - (BOOL)readDragData:(Pasteboard *)pboard
- {
- return YES;
- }
-
- - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
- {
- NXDragOperation sourceMask;
-
- sourceMask = [sender draggingSourceOperationMask];
-
- if (sourceMask & NX_DragOperationCopy)
- return NX_DragOperationCopy;
-
- return NX_DragOperationNone;
- }
-
- - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
- {
- return YES;
- }
-
- - (BOOL)prepareForDragOperation:(id <NXDraggingInfo>)sender
- {
- return YES;
- }
-
- - concludeDragOperation:(id <NXDraggingInfo>)sender
- {
- [self pasteFrom:[sender draggingPasteboard]];
- return self;
- }
-
- @end